package com.kelp.codetoolkit.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.psi.*
import com.intellij.psi.search.FilenameIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.util.PsiTreeUtil
import com.kelp.codetoolkit.enums.JavaBaseRefTypeEnum
import com.kelp.codetoolkit.enums.ServerPackageEnum
import com.kelp.codetoolkit.service.WriteFileService
import com.kelp.codetoolkit.ui.form.MethodGenerationDialog

/**
 * @author: liuyuebai
 * @date: 2025/7/5 16:22
 * @description: 在方法中生成方法
 */
class MethodToMethodAction:AnAction() {
    override fun actionPerformed(e: AnActionEvent) {
        val project = e.project ?: return
        val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
        val psiJavaFile = e.getData(PlatformDataKeys.PSI_FILE) as PsiJavaFile
        val offset = editor.caretModel.offset
        val elementAt = psiJavaFile.findElementAt(offset)?: return
        // 显示自定义弹窗，获取用户输入
        val dialog = MethodGenerationDialog(project)
        if (!dialog.showAndGet()) {
            // 用户取消操作，直接返回
            return
        }
        // 查找方法调用表达式
        val methodCall = PsiTreeUtil.getParentOfType(elementAt, PsiMethodCallExpression::class.java) ?: run {
            Messages.showErrorDialog(project, "Not a method call", "Error")
            return
        }
        val methodName = methodCall.methodExpression.referenceName?:""
        if (methodName.isBlank()){
            Messages.showErrorDialog(project, "Method Name is not NULL", "Error")
            return
        }
        val paramContentMap = mutableMapOf<String, Any>()
        paramContentMap.put("methodName",methodName)
        // 解析参数类型
        val importList = mutableSetOf<String>()
        val paramList = mutableListOf<Map<String,String>>()
        methodCall.argumentList.expressions.forEach { expr ->
            val paramTypeMap = mutableMapOf<String, String>()
            // 获取参数名称（通常是表达式的文本，如变量名）
            val paramName = expr.text
            val psiType = expr.type
            val presentableText = psiType?.presentableText?:""
            paramTypeMap.put("paramName", paramName)
            paramTypeMap.put("paramType",presentableText)
            paramList.add(paramTypeMap)
            if (expr is PsiMethodCallExpression){
                val fieldFromMethodCall = getFieldFromMethodCall(expr)
                if (fieldFromMethodCall != null) {
                    paramTypeMap.put("paramName", fieldFromMethodCall.name)
                }
                val canonicalText = fieldFromMethodCall?.type?.canonicalText?:""
                val typeEnum = JavaBaseRefTypeEnum.getByName(canonicalText)
                if (canonicalText.isNotBlank() && null == typeEnum){
                    importList.add(canonicalText)
                }
            }else if(isListType(expr)){
                val type = expr.type as PsiClassType
                val collection = type.rawType().canonicalText
                importList.add(collection)
                val paramData = type.parameters.getOrNull(0)?.canonicalText?:""
                val typeEnum = JavaBaseRefTypeEnum.getByName(paramData)
                if (paramData.isNotBlank() && null == typeEnum){
                    importList.add(paramData)
                }
            }else if(isSetType(expr)){
                val type = expr.type as PsiClassType
                val collection = type.rawType().canonicalText
                importList.add(collection)
                val paramData = type.parameters.getOrNull(0)?.canonicalText?:""
                val typeEnum = JavaBaseRefTypeEnum.getByName(paramData)
                if (paramData.isNotBlank() && null == typeEnum){
                    importList.add(paramData)
                }
            }else if(isMapType(expr)){
                val type = expr.type as PsiClassType
                val collection = type.rawType().canonicalText
                importList.add(collection)
                val paramKey = type.parameters.getOrNull(0)?.canonicalText?:""
                val paramKeyTypeEnum = JavaBaseRefTypeEnum.getByName(paramKey)
                if (paramKey.isNotBlank() && null == paramKeyTypeEnum){
                    importList.add(paramKey)
                }
                val paramValue = type.parameters.getOrNull(1)?.canonicalText?:""
                val paramValueTypeEnum = JavaBaseRefTypeEnum.getByName(paramValue)
                if (paramValue.isNotBlank() && null == paramValueTypeEnum){
                    importList.add(paramValue)
                }
            }else{
                val paramType = psiType?.canonicalText ?: "Object"
                val typeEnum = JavaBaseRefTypeEnum.getByName(paramType)
                if(null == typeEnum){
                    importList.add(paramType)
                }
            }
        }
        paramContentMap.put("paramList",paramList)
        val returnParamType = inferReturnTypeFromAssignment(methodCall)
        val returnParam = returnParamType?.presentableText?:"void"
        paramContentMap.put("returnParam",returnParam)
        val importReturn = returnParamType?.canonicalText?:""
        if (importReturn.isNotBlank()){
            importList.add(importReturn)
        }
        paramContentMap.put("importList",importList)

        val objectRef = methodCall.methodExpression.qualifier?.text?:""
        if (objectRef.isNotBlank()){
            handleInterface(objectRef,dialog,returnParam,methodName,paramList,project,methodCall,importList)
        }else{
            handleCurrentFile(psiJavaFile,dialog,returnParam,methodName,paramList,project,methodCall,importList)
        }

    }

    fun getFieldFromMethodCall(methodCall: PsiMethodCallExpression): PsiField? {
        val qualifierExpr = methodCall.methodExpression.qualifierExpression ?: return null
        val qualifierType = qualifierExpr.type ?: return null
        val project = methodCall.project

        val psiClass = JavaPsiFacade.getInstance(project).findClass(
            qualifierType.canonicalText,
            GlobalSearchScope.allScope(project)
        ) ?: return null

        val methodName = methodCall.methodExpression.referenceName ?: return null
        val method = psiClass.findMethodsByName(methodName, false).firstOrNull() ?: return null

        return getMethodReturnedField(method)
    }

    fun getMethodReturnedField(method: PsiMethod): PsiField? {
        val body = method.body ?: return null
        val returns = PsiTreeUtil.findChildrenOfType(body, PsiReturnStatement::class.java)
        for (retStmt in returns) {
            val refExpr = retStmt.returnValue as? PsiReferenceExpression ?: continue
            val resolved = refExpr.resolve()
            if (resolved is PsiField) {
                return resolved
            }
        }
        return null
    }

    private fun isListType(expr: PsiExpression): Boolean {
        val psiType = expr.type?:return false
        if (psiType is PsiClassType) {
            val rawType = psiType.rawType()
            return rawType.canonicalText == "java.util.List"
        }
        return false
    }

    private fun isSetType(expr: PsiExpression): Boolean {
        val psiType = expr.type?:return false
        if (psiType is PsiClassType) {
            val rawType = psiType.rawType()
            return rawType.canonicalText == "java.util.Set"
        }
        return false
    }

    private fun isMapType(expr: PsiExpression): Boolean {
        val psiType = expr.type?:return false
         if (psiType is PsiClassType) {
             val rawType = psiType.rawType()
             return rawType.canonicalText == "java.util.Map"
         }
        return false
    }

    // 从方法调用所在的赋值语句中推断返回类型
    private fun inferReturnTypeFromAssignment(methodCall: PsiMethodCallExpression): PsiType? {
        val variable = PsiTreeUtil.getParentOfType(methodCall, PsiLocalVariable::class.java)
        return variable?.type
    }

    private fun handleInterface(objectRef: String, dialog: MethodGenerationDialog, returnParam:String, methodName: String, paramList:List<Map<String, String>>, project:Project, methodCall: PsiExpression, importList:Set<String>) {
        if (objectRef.isBlank()){
            return
        }
        val javaPsiFacade = JavaPsiFacade.getInstance(project)
        val isService = ServerPackageEnum.isService(objectRef)
        if (isService) {
            val importPsiClasses = mutableSetOf<PsiClass>()
            importList.forEach { importName ->
                val importPsiClass = javaPsiFacade.findClass(importName, GlobalSearchScope.allScope(project))
                if (importPsiClass != null) {
                    importPsiClasses.add(importPsiClass)
                }
            }
            val variable = PsiResolveHelper.getInstance(project).resolveReferencedVariable(objectRef, methodCall.context)
            if (variable is PsiVariable) {
                val type = variable.type
                val findClass = javaPsiFacade.findClass(type.canonicalText, GlobalSearchScope.allScope(project))?:return
                val methodContent = handleInterfaceWriteMethod(dialog, returnParam, methodName, paramList)
                WriteFileService.getInstance(project).writeInterfaceMethod(project,methodContent,findClass)
                val psiJavaFile = findClass.containingFile as PsiJavaFile
                WriteFileService.getInstance(project).writeImportPackage(project, psiJavaFile,importPsiClasses)
                val childClass = ClassInheritorsSearch.search(findClass).findAll()
                val childMethodContent = handleClassWriteMethod(null,returnParam, methodName, paramList)
                for (implClass in childClass) {
                    WriteFileService.getInstance(project).writeInterfaceMethod(project,childMethodContent,implClass)
                    val childJavaFile = implClass.containingFile as PsiJavaFile
                    WriteFileService.getInstance(project).writeImportPackage(project, childJavaFile,importPsiClasses)
                }
                if (dialog.writeMapper){
                    val mapperContent = handleMapperInterfaceWriteMethod(dialog, returnParam, methodName, paramList)
                    val xmlContent = "\n    \n"
                    for (implClass in childClass) {
                        val superTypes = implClass.superTypes
                        // 遍历所有父类型
                        for (superType in superTypes) {
                            // 只处理类类型
                            if (superType !is PsiClassType) continue
                            val superClass = superType.resolve() ?: continue
                            // 检查是否是MyBatis-Plus的ServiceImpl
                            if (superClass.qualifiedName == "com.baomidou.mybatisplus.extension.service.impl.ServiceImpl") {
                                // 获取泛型参数
                                val typeArguments = superType.parameters
                                val mapper = typeArguments[0]
                                val mapperClass = javaPsiFacade.findClass(mapper.canonicalText, GlobalSearchScope.allScope(project))?:return
                                WriteFileService.getInstance(project).writeInterfaceMethod(project,mapperContent,mapperClass)
                                val mapperJavaFile = mapperClass.containingFile as PsiJavaFile
                                val paramPsiClass = javaPsiFacade.findClass("org.apache.ibatis.annotations.Param", GlobalSearchScope.allScope(project))
                                if (null != paramPsiClass) {
                                    importPsiClasses.add(paramPsiClass)
                                }
                                WriteFileService.getInstance(project).writeImportPackage(project, mapperJavaFile,importPsiClasses)
                                val xmlFiles = FilenameIndex.getVirtualFilesByName(mapper.presentableText+".xml",GlobalSearchScope.allScope(project))
                                val xmlVirtualFIle = xmlFiles.first()
                                WriteFileService.getInstance(project).writeXmlContentToFile(project,xmlVirtualFIle,xmlContent,methodName)
                            }
                        }
                    }
                }
            }
        }

    }

    private fun handleCurrentFile(psiJavaFile: PsiJavaFile, dialog: MethodGenerationDialog, returnParam:String, methodName: String, paramList:List<Map<String, String>>, project:Project, methodCall: PsiExpression, importList:Set<String>) {
        val javaPsiFacade = JavaPsiFacade.getInstance(project)
        val importPsiClasses = mutableSetOf<PsiClass>()
        importList.forEach { importName ->
            val importPsiClass = javaPsiFacade.findClass(importName, GlobalSearchScope.allScope(project))
            if (importPsiClass != null) {
                importPsiClasses.add(importPsiClass)
            }
        }
        val klass = psiJavaFile.classes[0]
        val methodContent = handleClassWriteMethod(dialog, returnParam, methodName, paramList)
        WriteFileService.getInstance(project).writeInterfaceMethod(project,methodContent,klass)
        WriteFileService.getInstance(project).writeImportPackage(project, psiJavaFile,importPsiClasses)
    }

    private fun handleInterfaceWriteMethod(dialog: MethodGenerationDialog,returnParam:String,methodName: String,paramList:List<Map<String, String>>): String{
        val methodContent = StringBuilder()
        // 拼接JavaDoc风格的方法注释
        methodContent.append("/**\n")
        methodContent.append(" *").append(dialog.methodComment).append("\n")
        methodContent.append(" */\n")
        methodContent.append(returnParam).append(" ").append(methodName).append("(")
        // 遍历参数列表
        paramList.forEachIndexed { index, paramMap ->
            // 假设每个map中存着参数的类型和名称，例如 {"type": "String", "name": "bankCode"}
            val paramType = paramMap["paramType"] ?: ""
            val paramName = paramMap["paramName"] ?: ""
            val paramStr = "$paramType $paramName"
            methodContent.append(paramStr)
            // 不是最后一个参数则加逗号
            if (index != paramList.lastIndex) {
                methodContent.append(", ")
            }
        }
        // 拼接方法结束的右括号
        methodContent.append(");")
        return methodContent.toString()
    }

    private fun handleMapperInterfaceWriteMethod(dialog: MethodGenerationDialog,returnParam:String,methodName: String,paramList:List<Map<String, String>>): String{
        val methodContent = StringBuilder()
        // 拼接JavaDoc风格的方法注释
        methodContent.append("/**\n")
        methodContent.append(" *").append(dialog.methodComment).append("\n")
        methodContent.append(" */\n")
        methodContent.append(returnParam).append(" ").append(methodName).append("(")
        // 遍历参数列表
        paramList.forEachIndexed { index, paramMap ->
            // 假设每个map中存着参数的类型和名称，例如 {"type": "String", "name": "bankCode"}
            val paramType = paramMap["paramType"] ?: ""
            val paramName = paramMap["paramName"] ?: ""
            val paramStr = "@Param(\"$paramName\")$paramType $paramName"
            methodContent.append(paramStr)
            // 不是最后一个参数则加逗号
            if (index != paramList.lastIndex) {
                methodContent.append(", ")
            }
        }
        // 拼接方法结束的右括号
        methodContent.append(");")
        return methodContent.toString()
    }

    private fun handleClassWriteMethod(dialog: MethodGenerationDialog?,returnParam:String,methodName: String,paramList:List<Map<String, String>>):String{
        val methodContent = StringBuilder()
        //如果是接口实现方法不需要注释，因为接口方法已经添加了注释
        if (null != dialog) {
            // 拼接JavaDoc风格的方法注释
            methodContent.append("/**\n")
            methodContent.append(" *").append(dialog.methodComment).append("\n")
            methodContent.append(" */\n")
        }
        methodContent.append("public ").append(returnParam).append(" ").append(methodName).append("(").append(" ")
        // 遍历参数列表
        paramList.forEachIndexed { index, paramMap ->
            // 假设每个map中存着参数的类型和名称，例如 {"type": "String", "name": "bankCode"}
            val paramType = paramMap["paramType"] ?: ""
            val paramName = paramMap["paramName"] ?: ""
            val paramStr = "$paramType $paramName"
            methodContent.append(paramStr)
            // 不是最后一个参数则加逗号
            if (index != paramList.lastIndex) {
                methodContent.append(", ")
            }
        }
        // 拼接方法结束的右括号
        methodContent.append("){\n")
        methodContent.append("\n")
        methodContent.append("}")
        return methodContent.toString()
    }

    private fun handleXmlWriteMethod(returnParam:String,methodName: String,paramList:List<Map<String, String>>):String{
        val methodContent = StringBuilder()
        methodContent.append("<select id =\"").append(methodName).append("\" ").append("resultType=\"\">")
        methodContent.append("\n")
        methodContent.append("</select>")
        return methodContent.toString()
    }


}